#include "mbdexportedit.h"

#include <QCheckBox>
#include <QFileDialog>
#include <QMessageBox>
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>

#include <mbdTreeNode.h>


struct MBDExportEditPrivate
{
    MBDExportEditPrivate() {}

    void setUiInfo(const QVariantMap &info) {
        for (auto it = tableKeyUis.begin(); it != tableKeyUis.end(); ++it) {
            if (!info.contains(it.key())) continue;
            const auto v = info[it.key()].toString();
            if (auto le = qobject_cast<QLineEdit*>(it.value())) {
                le->setText(v);
            } else if (auto chBox = qobject_cast<QCheckBox*>(it.value())) {
                auto state = v == "1" ? Qt::Checked : Qt::Unchecked;
                chBox->setChecked(state);
            }
        }
    }

    QVariantMap uiInfo() const {
        QVariantMap info;
        for (auto it = tableKeyUis.begin(); it != tableKeyUis.end(); ++it) {
            QVariant v;
            if (auto le = qobject_cast<QLineEdit*>(it.value())) {
                v = le->text();
            } else if (auto chBox = qobject_cast<QCheckBox*>(it.value())) {
                v = chBox->isChecked() ? "1" : "0";
            }
            info[it.key()] = v;
        }
        return info;
    }

    void resetUi() {
        for (auto it = tableKeyUis.begin(); it != tableKeyUis.end(); ++it) {
            if (auto le = qobject_cast<QLineEdit*>(it.value())) {
                le->setText("");
            } else if (auto chBox = qobject_cast<QCheckBox*>(it.value())) {
                auto state = Qt::Unchecked;
                chBox->setChecked(state);
            }
        }
    }

    QVariantMap flyByName(const QString& name) const {
        if (name.isEmpty()) return {};
        for (const auto& fly : selectTables) {
            auto n = fly["Name"].toString();
            if (n == name) return fly;
        }
        return {};
    }


    QMap<QString, QWidget*> tableKeyUis;
    QList<QVariantMap> selectTables;

};

MBDExportEdit::MBDExportEdit(QWidget *parent):
    IoEditFrameInterface(parent),
    ui(new Ui::MBDExportEdit)

{
    ui->setupUi(this);
    m_d = new MBDExportEditPrivate();

    m_d->tableKeyUis = {{"Name", ui->lineEdit_name},
        {"Laminates",ui->lineEdit_laminate},
        // {"Start Component", ui->lineEdit_status},
        // {"LastOkRunTime", ui->lineEdit_dp},
        // {"LastOkRunUser", ui->lineEdit_pb},
        // {"DesignBoundary", ui->lineEdit_dp},
        // {"TreeWriter", ui->checkBox_colorPlyGeo}
    };

    connect(ui->pushButton_selectLaminates, &QPushButton::clicked, this, [this](){
        QMap<QString, QString> headerToDbFieldMap;
        // headerToDbFieldMap["Document"] = "Name";    // 根据语义近似映射
        headerToDbFieldMap["Net Analysis Status"] = "NetAnalysisStatus";
        headerToDbFieldMap["Name"] = "Name";
        headerToDbFieldMap["Step"] = "Step";
        headerToDbFieldMap["Center of Gravity (mm)"] = "COG";
        headerToDbFieldMap["Net Area (mm²)"] = "Area";
        headerToDbFieldMap["Net Weight (kg)"] = "Weight";
        headerToDbFieldMap["Net Cost"] = "Cost";
        headerToDbFieldMap["Net Perimeter (mm)"] = "Perimeter";
        headerToDbFieldMap["Zone-Based Weight (kg)"] = "ZoneBasedWeight";

        static const QStringList tableHeaders = {
            "Net Analysis Status",
            "Name",
            "Step",
            "Center of Gravity (mm)",
            "Net Area (mm²)",
            "Net Weight (kg)",
            "Net Cost",
            "Net Perimeter (mm)",
            "Zone-Based Weight (kg)"
        };

        showDlg(ui->lineEdit_laminate, LAMINATETABLENAME,headerToDbFieldMap,tableHeaders);

    });

    initUI();

    connect(ui->pushButton_generate, &QPushButton::clicked, this, &MBDExportEdit::onGenerate);

}

MBDExportEdit::~MBDExportEdit()
{
    delete m_d;
    delete ui;
}

void MBDExportEdit::reInitUI()
{
    ui->lineEdit_name->setEnabled(true);
    initUI();
}

void MBDExportEdit::initUI()
{
    m_d->resetUi();
}

QString MBDExportEdit::title()
{
    return ui->lineEdit_name->text();
}

void MBDExportEdit::editUI(const QString &name)
{
    Sqltool::instance().changeInfoDb();
    auto info = Sqltool::instance().selectOne(FIBMBDTABLENAME, {"Name", name});
    m_d->setUiInfo(info);
    ui->lineEdit_name->setEnabled(false);
}

bool MBDExportEdit::save()
{
    QVariantMap info = this->info();
    QString tableName = FIBMBDTABLENAME;
    Sqltool::instance().changeInfoDb();

    auto name = info["Name"].toString();
    auto names = Sqltool::instance().selectAllNamesByTableName(tableName);
    if (names.contains(name)) {
        QMessageBox::information(this, "提示", tr("当前名称已经存在，请重新修改名称。"));
        return false;
    }
    Sqltool::instance().insertDb(tableName, info);
    return true;
}

bool MBDExportEdit::update()
{
    QVariantMap info = this->info();
    QString tableName = FIBMBDTABLENAME;
    Sqltool::instance().changeInfoDb();
    auto name = info["Name"].toString();
    QVariantMap c{{"Name", name}};
    Sqltool::instance().updateDb(tableName, info, c);
    return true;
}

void MBDExportEdit::createBaseOn(const QString &name)
{
    ui->lineEdit_name->setEnabled(true);
    Sqltool::instance().changeInfoDb();
    auto info = Sqltool::instance().selectOne(FIBMBDTABLENAME, {"Name", name});
    m_d->setUiInfo(info);
}

QVariantMap MBDExportEdit::info()
{
    return m_d->uiInfo();
}

//展示选择其他对象界面
void MBDExportEdit::showDlg(QLineEdit *le,
                            const QString& tableName,
                            const QMap<QString, QString>& header2Sqlkeys,
                            const QStringList& tableHeaders)
{
    IOSelectDialog dlg(this);

    QList<QVariantMap> sqlModels = Sqltool::instance().selectAllByTableName(tableName);

    QVector<QVariantMap> tables;
    for (const auto& model : sqlModels) {

        QVariantMap info;
        for (auto it = header2Sqlkeys.begin(); it != header2Sqlkeys.end(); ++it) {
            info[it.key()] = model[it.value()];
        }
        tables.push_back(info);
    }
    QString selectedComponets = le->text();
    QStringList strlist = {};
    if (!selectedComponets.isEmpty())
        strlist = selectedComponets.split(",");
    dlg.setTableData(tableHeaders, tables, strlist);
    dlg.setMultipleChoice();
    if (dlg.exec() == QDialog::Accepted) {
        auto componets = dlg.selectedComponets();
        if (componets.size() > 0) {
            QString leText = QString("%1").arg(componets.join(","));
            le->setText(leText);
        }
    }

}

struct TreeInfo
{
    TreeInfo(const std::string& k,
             const std::string& tk,
             ValueType vType,
             bool isCk = false,
             const std::string& pName = "",
             bool ap = false): key(std::move(k)),
        tableKey(std::move(tk)),
        valueType(vType),
        isCheck(isCk),
        parentName(pName),
        appendName(ap)    {

    }

    std::string key;
    std::string tableKey;
    ValueType valueType;
    bool isCheck = false;
    std::string parentName;
    bool appendName = false;
};


// 用于转换TreeNode到JSON的辅助函数
QJsonObject treeNodeToJson(const TreeNode* node) {
    QJsonObject obj;
    obj["key"] = QString::fromStdString(node->key);
    obj["value"] = QString::fromStdString(node->value);
    obj["nodeName"] = QString::fromStdString(node->nodeName);
    obj["valueType"] = static_cast<int>(node->valueType);

    QJsonArray children;
    for (const auto& child : node->childNodes) {

        // child->key;
        // child->nodeName;
        children.append(treeNodeToJson(child.get()));
        //

    }
    obj["childNodes"] = children;
    return obj;
}

// 导出JSON到文件的辅助函数
bool exportToJson(const TreeNode* node, const QString& filePath) {
    QJsonObject root = treeNodeToJson(node);
    QJsonDocument doc(root);

    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly)) {
        qWarning() << "无法打开文件进行写入:" << filePath;
        return false;
    }

    file.write(doc.toJson(QJsonDocument::Indented));
    file.close();
    return true;
}

void MBDExportEdit::onGenerate() {

    auto lamText = ui->lineEdit_laminate->text();
    const auto lams = lamText.split(",");
    if (lams.empty()) return;
    auto name = ui->lineEdit_name->text();
    if (name.isEmpty()) return;

    auto mbdNode = std::make_unique<TreeNode>();
    mbdNode->key = name.toStdString();
    mbdNode->valueType = ValueType::GEOMSET_TYPE;
    auto geoNode = std::make_unique<TreeNode>();
    geoNode->valueType = ValueType::GEOMSET_TYPE;
    geoNode->key="CCDS Base Geometry";
    Sqltool::instance().changeInfoDb();

    const std::vector<TreeInfo> lamTreeInfos{
        TreeInfo("Sequence", "Sequence", STRING_TYPE),
        TreeInfo("Step",     "Step", STRING_TYPE),
        TreeInfo("Surface", "LayupSurface", BOUNDARY_TYPE, true, "", true),
        TreeInfo("Net Broundary Curve 1", "NetBoundary", BOUNDARY_TYPE,true, "", true),
        TreeInfo("Extended Broundary Curve 1", "ExtBoundany", BOUNDARY_TYPE, true, "", true)
    };

    const std::vector<TreeInfo> plyTreeInfos{
        TreeInfo("Sequence", "Sequence", STRING_TYPE),
        TreeInfo("Step",     "Step", STRING_TYPE),
        TreeInfo("Material", "Material", STRING_TYPE),
        TreeInfo("Specified Orientation",     "SpecifiedOrientation", STRING_TYPE),
        TreeInfo("Reference Rosette", "Rosette", STRING_TYPE, true),
        TreeInfo("Net Broundary", "NetBoundary", BOUNDARY_TYPE, true, "", true),
        TreeInfo("Extended Broundary", "ExtBoundany", BOUNDARY_TYPE, true, "", true),
        TreeInfo("Origin",     "Origin", BOUNDARY_TYPE, true),
        TreeInfo("Direction",  "Direction", STRING_TYPE, true),
    };

    const std::vector<TreeInfo> coreTreeInfos{
        TreeInfo("Sequence", "Sequence", STRING_TYPE),
        TreeInfo("Step",     "Step", STRING_TYPE),
        TreeInfo("Material", "Material", STRING_TYPE),
        TreeInfo("Origin",     "Origin", BOUNDARY_TYPE, true),
        TreeInfo("Broundary",  "NetBoundary", STRING_TYPE, true)
    };


    const std::vector<TreeInfo> lamgeoTreeInfos{
        TreeInfo("Surface", "LayupSurface", BOUNDARY_TYPE, true, "Layup Surface", true),
        TreeInfo("Net Broundary Curve 1", "NetBoundary", BOUNDARY_TYPE, true, "Net Boundary", true),
        TreeInfo("Extended Broundary Curve 1", "ExtBoundany", BOUNDARY_TYPE, true, "Extened Boundany", true)
    };

    const std::vector<TreeInfo> plygeoTreeInfos{
        TreeInfo("Net Broundary", "NetBoundary", BOUNDARY_TYPE, true, "Net Broundary", true),
        TreeInfo("Extended Broundary", "ExtBoundany", BOUNDARY_TYPE, true, "Extended Broundary", true),
        TreeInfo("Origin",     "Origin", BOUNDARY_TYPE,true,"Origin"),
        TreeInfo("Direction",  "Direction", STRING_TYPE,true,"Direction"),
        TreeInfo("Net Skin",   "NetSkin", BOUNDARY_TYPE,true,"Net Skin"),
        TreeInfo("Extended Skin",  "ExtendedSkin", STRING_TYPE,true,"Extended Skin"),
    };


    const std::vector<TreeInfo> coregeoTreeInfos{
        TreeInfo("Origin",     "Origin", BOUNDARY_TYPE,true,"Origin"),
        TreeInfo("Boundary.1",  "ExtBoundary", BOUNDARY_TYPE, true, "Boundary", true),
    };

    const std::vector<TreeInfo> rosettegeoTreeInfos{
        TreeInfo("Origin",     "Origin", BOUNDARY_TYPE,true,"Origin"),
        TreeInfo("Direction",  "Direction", STRING_TYPE,true,"Direction"),
        TreeInfo("",  "Name", STRING_TYPE,false,"", true),
    };



    const QStringList tableNames{
        FIBADDSKINCORE,
        FIBPARAMETRICCONSTANTCORE,
        FIBPARAMETRICSTEPCORE,
        FIBPARAMETRICVARIABLECORE
    };

    auto addChildNodes = [](const QVariantMap& tableInfos,
                            const std::vector<TreeInfo>& treeInfos,
                            TreeNode* parentNode,
                            const QString& nodeName,
                            const std::string& parentName){
        for (const auto& info : treeInfos) {
            auto v = tableInfos.value(QString::fromStdString(info.tableKey)).toString();
            if ((info.isCheck && !v.isEmpty()) || !info.isCheck) {
                auto key = info.key;
                if (info.appendName) {
                    key = parentName + " " + key;
                }
                auto node = parentNode->addChild(key, v.toStdString());
                node->valueType = info.valueType;
                node->nodeName = nodeName.toStdString();
            }
        }
    };

    auto addGeoChildNodes = [](const QVariantMap& tableInfos,
                               const std::vector<TreeInfo>& treeInfos,
                               TreeNode* parentNode,
                               const QString& nodeName,
                               const std::string& parentName){
        for (const auto& info : treeInfos) {
            auto v = tableInfos.value(QString::fromStdString(info.tableKey)).toString();
            if ((info.isCheck && !v.isEmpty()) || !info.isCheck) {
                auto pNode = parentNode->addChild(info.parentName, "");
                pNode->nodeName = nodeName.toStdString();

                auto key = info.key;
                if (info.appendName) {
                    key = parentName + " " + key;
                }
                auto node = pNode->addChild(key, v.toStdString());
                node->valueType = info.valueType;
                node->nodeName = nodeName.toStdString();
            }
        }
    };


    for (const auto& lamName : lams) {
        auto lam = Sqltool::instance().selectOne(LAMINATETABLENAME, {"Name", lamName});
        if (lam.empty()) continue;
        const auto lName = lamName.toStdString();
        auto lamChildNode = mbdNode->addChild(lName, "");
        lamChildNode->valueType = ValueType::GEOMSET_TYPE;
        lamChildNode->nodeName = LAMINATETABLENAME;
        addChildNodes(lam, lamTreeInfos, lamChildNode, LAMINATETABLENAME, lName);

        // CCDS BaseGeometric
        auto lamGeoNode = geoNode->addChild(lamName.toStdString(), "");
        lamGeoNode->nodeName = LAMINATETABLENAME;
        lamGeoNode->valueType = ValueType::GEOMSET_TYPE;
        addGeoChildNodes(lam, lamgeoTreeInfos, lamGeoNode, LAMINATETABLENAME, lName);

        QSet<QString> rosetteNames;
        auto plys = Sqltool::instance().selectData(FLYTABLENAME, {"Parent", lamName});
        for (const auto& ply : plys) {
            const auto& plyName = ply["Name"].toString().toStdString();
            auto plyNode = lamChildNode->addChild(plyName, "");
            plyNode->nodeName = FLYTABLENAME;
            plyNode->valueType = ValueType::GEOMSET_TYPE;
            addChildNodes(ply, plyTreeInfos, plyNode, FLYTABLENAME, plyName);

            // CCDS BaseGeometric
            auto plyGeoNode = lamGeoNode->addChild(plyName, "");
            plyGeoNode->nodeName = FLYTABLENAME;
            plyGeoNode->valueType = ValueType::GEOMSET_TYPE;
            addGeoChildNodes(ply, plygeoTreeInfos, plyGeoNode, FLYTABLENAME, plyName);


            const auto& rosetteName = ply["Rosette"].toString();
            rosetteNames.insert(rosetteName);
        }

        for (const auto& rn : rosetteNames) {
            if (rn.isEmpty()) continue;
            const auto rnName = rn.toStdString();
            lamChildNode->addChild(rnName, rnName);

            auto rosetteGeoNode = lamGeoNode->addChild(rnName, "");
            rosetteGeoNode->nodeName = FIBROSETTE;
            auto rosette = Sqltool::instance().selectOne(FIBROSETTE, {"Name", rn});
            addGeoChildNodes(rosette, rosettegeoTreeInfos, rosetteGeoNode, FIBROSETTE, rnName);
        }

        for (const auto& tableName : tableNames) {
            auto cores = Sqltool::instance().selectData(tableName, {"Parent", lamName});
            for (const auto& core : cores) {
                const auto& coreName = core["Name"].toString().toStdString();
                auto coreNode = lamChildNode->addChild(coreName, "");
                coreNode->nodeName = tableName.toStdString();
                coreNode->valueType = ValueType::GEOMSET_TYPE;
                addChildNodes(core, coreTreeInfos, coreNode, tableName, coreName);

                // CCDS BaseGeometric
                auto coreGeoNode = lamGeoNode->addChild(coreName, "");
                coreGeoNode->nodeName = tableName.toStdString();
                coreGeoNode->valueType = ValueType::GEOMSET_TYPE;
                addGeoChildNodes(core, coreTreeInfos, coreGeoNode, tableName, coreName);
            }
        }
    }

#if _DEBUG
    exportToJson(mbdNode.get(), "testmbdNode.json");
    exportToJson(geoNode.get(), "testmGeoNode.json");
#endif

    CADInterface::instance().fibCreateMBD(std::move(mbdNode),std::move(geoNode));

}

